/*
 *
 * Copyright (C) 2020 iQIYI (www.iqiyi.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.qiyi.tm.taskmanager;





import com.qiyi.tm.taskmanager.iface.ITask;
import com.qiyi.tm.taskmanager.other.TMLog;
import com.qiyi.tm.taskmanager.struct.DataMaker;
import ohos.eventhandler.EventRunner;


import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class Job implements ITask {

    // 存储当前任务自己的数据
    private HashMap<String, Object> dataMap;
    // 存储父任务传递的数据
    private HashMap<String, Object> parentDataMap;
    private static final String TAG = "TM_Job";

    protected String name = "";
    protected int taskId;
    final static AtomicInteger genTaskId = new AtomicInteger();
    int taskPriority;
    protected int bindActivityHash;
    short groupId;

    public static final int TASK_PRIORITY_MAX = 100;
    public static final int TASK_PRIORITY_MIN = -100;


    public Job(String name) {
        this.name = name;
        taskId = genTaskId.incrementAndGet();
        TaskRecorder.registerTaskName(taskId, name);
    }

    public Job() {
        taskId = genTaskId.incrementAndGet();
    }

    public Job(int tid) {
        this.taskId = tid;
    }


    public Job(String name, int tid) {
        this.name = name;
        this.taskId = tid;
        TaskRecorder.registerTaskName(taskId, name);
    }

    public String getName() {
        return name;
    }


    public int getTaskId() {
        return taskId;
    }

    public Job setName(String name) {
        this.name = name;
        TaskRecorder.registerTaskName(taskId, name);
        return this;
    }

    public Job setTaskID(int id) {
        taskId = id;
        return this;
    }

    public int getTaskPriority() {
        return taskPriority;
    }

    /**
     * default 0：
     *
     * @param priority : integer
     * @return
     */
    public Job setTaskPriority(int priority) {
        taskPriority = priority;
        if (priority > TASK_PRIORITY_MAX || priority < TASK_PRIORITY_MIN) {
            throw new IllegalArgumentException("task : " + getName() + " task priority should be within -100 ~100");
        }
        return this;
    }


    /**
     * support data pass: cache data generated by this task
     *
     * @param data
     */
    public void putData(String key, Object data) {
        if (dataMap == null) {
            dataMap = new HashMap<>();
        }
        dataMap.put(key, data);
    }

    public void putData(Object data) {
        putData(String.valueOf(taskId), data);
    }

    //copy data from task  需要解决多依赖的情况下， 继承自parent 的部分 很可能会有相同的key 的问题
    // 父任务的数据信息不会被传递进来;
    synchronized void copyData(Job task) {
        if (task != null && task.dataMap != null) {
            if (TMLog.isDebug()) {
                //check data with same key：检测是不是多个父亲任务都传递了相同的数据key 进来
                HashMap<String, Object> map = task.dataMap;
                Set<String> keys = map.keySet();
                for (String var : keys) {
                    // fix bug: npe
                    String msg = "Task " + task.getName() + " id: " + task.getTaskId() + " data key  " + var;
                    if (parentDataMap != null && parentDataMap.containsKey(var)) {
                        msg += " conflict with other tasks";
                    } else if (dataMap != null && dataMap.containsKey(keys)) {
                        msg += " conflict with " + getName();
                    } else {
                        continue;
                    }
                    if (TaskManager.isDebugCrashEnabled()) {
                        throw new IllegalArgumentException(msg);
                    } else {
                        TMLog.e(TAG, msg);
                    }
                }
            }
            if (parentDataMap == null) {
                parentDataMap = new HashMap<String, Object>();
            }

            String taskIdKey = String.valueOf(task.getTaskId());
            if (!parentDataMap.containsKey(taskIdKey)) {
                parentDataMap.put(taskIdKey, null);
                parentDataMap.putAll(task.dataMap);
            }

        }
    }


    synchronized void passData(int taskId, Object var) {
        if (var != null) {
            if (var.getClass() == DataMaker.class) {
                DataMaker maker = (DataMaker) var;
                if (parentDataMap == null) {
                    parentDataMap = maker.get();
                } else {
                    parentDataMap.putAll(maker.get());
                }
            } else {
                if (parentDataMap == null) {
                    parentDataMap = new HashMap<String, Object>();
                }
                String taskIdKey = String.valueOf(taskId);
                parentDataMap.put(taskIdKey, var);
            }

        }
    }


    public Object getData(int taskId) {
        return getData(String.valueOf(taskId));
    }

    //  优先获取父任务中的数据;  当父任务中没有数据 才获取自己的数据
    public Object getData(String key) {
        Object var = null;
        if (parentDataMap != null) {
            var = parentDataMap.get(key);
        }
        if (var == null && dataMap != null) {
            var = dataMap.get(key);
        }
        return var;
    }

    public <T> T getData(String key, Class<T> cls) {
        Object data = getData(key);
        if (cls != null && data != null && cls.isAssignableFrom(data.getClass())) {
            return (T) data;
        }
        return null;
    }

    public <T> T getData(int taskId, Class<T> cls) {
        return getData(String.valueOf(taskId), cls);
    }


    /**
     * 如果是在主线程抛出，讲在主线程执行， 子线程抛出 将在子线程执行;
     */
    public void post() {
        if (EventRunner.getMainEventRunner() ==EventRunner.create()) {
            postUI();
        } else {
            postAsync();
        }
    }


    protected boolean isUIThread() {
        return EventRunner.create()== EventRunner.getMainEventRunner();
    }

    public Job setGroupObject(Object group) {
        TM.crashIf(group instanceof Integer || group instanceof Long || group instanceof Short,
                "Please don't use Long , Integer, Short as Group Object identifier ");
        this.groupId = TaskRecorder.generateGroupId(group);
        return this;
    }

    /**
     * Group id can only be generated by TM.  refer to :TaskRecorder.generateGroupId(group)
     *
     * @param groupId
     * @return
     */
    public Job setGroupId(short groupId) {
        TM.crashIf(groupId > TM.GROUP_ID_RANGE, "group id should be < 0xfff");
        this.groupId = groupId;
        return this;
    }


    public int getGroupId() {
        return groupId;
    }

    //TM 先会执行延时等待，然后才去执行依赖逻辑检测;因此当这里被调用的时候，延迟条件已经满足了。在这里不考虑延时执行部分的问题。
    //10.8.0 新增idle run: 新增检测 如果是idle task : 那么只有满足idle run 条件的才进就绪队列
    abstract Task onDependantTaskFinished(Task finishedTask, int taskId);
//    public abstract void postUI();
//    public abstract void postAsync();


    protected void clear() {
        if (dataMap != null) {
            dataMap.clear();
        }
        if (parentDataMap != null) {
            parentDataMap.clear();
        }
    }
}
